package com.auxgroup.hr.front.service.process.impl;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import com.auxgroup.hr.front.commons.Constant;
import com.auxgroup.hr.front.commons.enums.StepStatusEnum;
import com.auxgroup.hr.front.commons.utils.ObjectFieldUtils;
import com.auxgroup.hr.front.commons.utils.Underline2Camel;
import com.auxgroup.hr.front.commons.utils.WebServiceClient;
import com.auxgroup.hr.front.commons.utils.WebServiceUtils;
import com.auxgroup.hr.front.commons.vo.AuthorityFiledVO;
import com.auxgroup.hr.front.commons.vo.UserDTO;
import com.auxgroup.hr.front.dao.domain.ProcessMapRespVO;
import com.auxgroup.hr.front.dao.domain.PsCAppStepInst;
import com.auxgroup.hr.front.dao.domain.PsCAppStepInstPrimary;
import com.auxgroup.hr.front.dao.domain.PsCAppStepPmi;
import com.auxgroup.hr.front.dao.domain.PsCAppUserInst;
import com.auxgroup.hr.front.dao.domain.PsCWfCatalog;
import com.auxgroup.hr.front.dao.domain.PsCWfStatusVw;
import com.auxgroup.hr.front.dao.repository.PsCAppStepInstRepository;
import com.auxgroup.hr.front.dao.repository.PsCAppUserInstRepository;
import com.auxgroup.hr.front.dao.repository.PsCWfCatalogRepository;
import com.auxgroup.hr.front.dao.repository.PsCWfStatusVwRepository;
import com.auxgroup.hr.front.dozer.IObjectMapper;
import com.auxgroup.hr.front.service.comm.SysDictService;
import com.auxgroup.hr.front.service.process.ProcessService;
import com.auxgroup.hr.front.service.process.PsCAppStepPmiService;
import com.auxgroup.hr.front.ws.ProcessApplyQueDTO;
import com.auxgroup.hr.front.ws.ProcessApproveQueDTO;
import com.auxgroup.hr.front.ws.SoapXmlBuild;

/**
 * @author lvliuhao
 * @version 创建时间：2018年5月31日 下午10:00:00
 * 流程操作类
 */
@Service("processService")
public class ProcessServiceImpl implements ProcessService {

    private static final Logger logger = LoggerFactory.getLogger(ProcessServiceImpl.class);

    @Resource(name = "dozerMapper")
    protected IObjectMapper objectMapper;

    @Autowired
    private SysDictService sysDictService;

    @Autowired
    private PsCAppUserInstRepository psCAppUserInstRepository;

    @Autowired
    public PsCAppStepPmiService psCAppStepPmiService;

    @Autowired
    public PsCAppStepInstRepository psCAppStepInstRepository;

    @Autowired
    public PsCWfStatusVwRepository psCWfStatusVwRepository;

    @Autowired
    public PsCWfCatalogRepository psCWfCatalogRepository;


    @Value("${ws.processService.approve.address}")
    private String processURL;

    @Value("${ws.processService.approve.soapAction}")
    private String approvalsoapAction;

    @Value("${ws.processService.gettrannum.soapAction}")
    private String getTranNumsoapAction;

    @Value("${ws.processService.apply.soapAction}")
    private String processApplysoapAction;
    @Value("${ws.processService.leavetime.soapAction}")
    private String processLeaveTimesoapAction;


    @Override
    public Map<String, String> approve(ProcessApproveQueDTO requestDTO) {
        StringBuilder soapXml = new StringBuilder();
        SoapXmlBuild.buildApprovalSoapXml(soapXml, requestDTO);
        String responXml = WebServiceClient.doPostSoap(processURL, soapXml.toString(), approvalsoapAction);
        return WebServiceUtils.handleRespXml(responXml, "C_WFL_APPROVAL_RESP");
    }


    @Override
    public String calLeaveTime(
            String emplid, String leaveType, String applInfo,
            String appDt, String acrossDay, String beginDt,
            String leaDayA, String endDt, String leaDayB) {
        StringBuilder soapXml = new StringBuilder();
        SoapXmlBuild.buildLeavetimeSoapXml(soapXml, emplid, leaveType, applInfo, appDt, acrossDay, beginDt,
                leaDayA, endDt, leaDayB);
        String respXml = WebServiceClient.doPostSoap(processURL, soapXml.toString(), processLeaveTimesoapAction);
        if (StringUtils.isNotBlank(respXml)) {
            Map<String, String> transNumMap = WebServiceUtils.handleRespXml(respXml, "C_CAL_LEAVE_RES");
            if (transNumMap != null && !transNumMap.isEmpty()) {
                return transNumMap.get("C_MSG_CODE");
            }
        }
        return null;
    }

    @Override
    public String getTranNum() {
        StringBuilder soapXml = new StringBuilder();
        SoapXmlBuild.buildGetTranNumSoapXml(soapXml);
        String respXml = WebServiceClient.doPostSoap(processURL, soapXml.toString(), getTranNumsoapAction);
        if (StringUtils.isNotBlank(respXml)) {
            Map<String, String> transNumMap = WebServiceUtils.handleRespXml(respXml, "C_GET_TRANSACTION_NBR_RESP");
            if (!transNumMap.isEmpty()) {
                return transNumMap.get("C_TRANSACTION_NBR");
            }
        }
        return null;
    }


    @Override
    public Map<String, String> apply(ProcessApplyQueDTO requestDTO) {
        StringBuilder soapXml = new StringBuilder();
        SoapXmlBuild.buildProcessApplySoapXml(soapXml, requestDTO);
        System.out.println(soapXml.toString());
        String respXml = WebServiceClient.doPostSoap(processURL, soapXml.toString(), processApplysoapAction);
        return WebServiceUtils.handleRespXml(respXml, "C_WFL_APPLY_RESP");
    }


    /* (non-Javadoc)
     * @see ProcessService#processMap(java.lang.String)
     */
    @Override
    public List<List<ProcessMapRespVO>> processMap(String transNum) {
        List<Object> results = sysDictService.getObjectListFromSql(Constant.PROCESSMAP_QUERY_BY_TRANSNUM_SQL, new String[]{transNum}, ProcessMapRespVO.class);
        List<ProcessMapRespVO> respVOs = new ArrayList<ProcessMapRespVO>();
        for (Object obj : results) {
            respVOs.add((ProcessMapRespVO) obj);
        }
        if (respVOs != null && !respVOs.isEmpty()) {
            Map<Integer, List<ProcessMapRespVO>> respVOMap = new TreeMap<Integer, List<ProcessMapRespVO>>(new MapKeyComparator());
            List<ProcessMapRespVO> respVoList;
            for (ProcessMapRespVO respVo : respVOs) {
                if (respVOMap.containsKey(Integer.valueOf(respVo.getcStepSortNbr()))) {
                    respVOMap.get(Integer.valueOf(respVo.getcStepSortNbr())).add(respVo);
                } else {
                    respVoList = new ArrayList<ProcessMapRespVO>();
                    respVoList.add(respVo);
                    respVOMap.put(Integer.valueOf(respVo.getcStepSortNbr()), respVoList);
                }
            }
            List<List<ProcessMapRespVO>> respVOlists = new ArrayList<List<ProcessMapRespVO>>(respVOMap.values());
            return respVOlists;
        }
        return null;
    }

    /**
     * 已处理节点
     *
     * @param transNum
     * @return
     */
    @Override
    public List<ProcessMapRespVO> processedNode(String transNum) {
        List<Object> results = sysDictService.getObjectListFromSql(Constant.PROCESSMAP_QUERY_BY_TRANSNUM_SQL, new String[]{transNum}, ProcessMapRespVO.class);
        List<ProcessMapRespVO> respVOs = new ArrayList<ProcessMapRespVO>();
        for (Object obj : results) {
            respVOs.add((ProcessMapRespVO) obj);
        }
        if (respVOs != null) {
            return respVOs.stream().filter(pmr ->
                    StringUtils.equalsIgnoreCase(pmr.getcStepStatus(), "A") ||
                            StringUtils.equalsIgnoreCase(pmr.getcStepStatus(), "S")
            ).sorted((a, b) -> a.getcStepSortNbr().compareTo(b.getcStepSortNbr())
            ).collect(Collectors.toList());
        }
        return null;
    }

    public class MapKeyComparator implements Comparator<Integer> {

        @Override
        public int compare(Integer int1, Integer int2) {
            return int1.compareTo(int2);
        }
    }


    @Override
    public Set<AuthorityFiledVO> buildAuthFieldVo(Object processHead, UserDTO user, String transNum, Set<String> authFileds) {
        List<PsCAppStepPmi> stepPmis = getStepPmis(transNum, user);
        logger.info("wfprchdr process PsCAppStepPmi info:{}", stepPmis);
        return buildAuthFieldVos(stepPmis, processHead, authFileds);
    }

    @Override
    public Set<AuthorityFiledVO> buildAuthFieldVo(Object processHead, String stepNbr, String transNum, Set<String> authFileds) {
        if (StringUtils.isEmpty(stepNbr)) {
            stepNbr = "1";
        }
        List<PsCAppStepPmi> stepPmis = getStepPmis(transNum, new BigDecimal(stepNbr));
        logger.info("wfprchdr process PsCAppStepPmi info:{}", stepPmis);
        return buildAuthFieldVos(stepPmis, processHead, authFileds);
    }


    private Set<AuthorityFiledVO> buildAuthFieldVos(List<PsCAppStepPmi> stepPmis, Object processHead, Set<String> authFileds) {
        Set<AuthorityFiledVO> filedVos = new HashSet<AuthorityFiledVO>();
        AuthorityFiledVO filedVo = null;
        String camelField;
        // 如果数据库权限字段为空。 按照全部为显示，不可编辑来控制
        if (stepPmis == null || stepPmis.isEmpty()) {
            for (String fieldName : authFileds) {
                filedVo = new AuthorityFiledVO();
                filedVo.setFieldKey(fieldName);
                filedVo.setFieldValue(ObjectFieldUtils.getFieldValueByName(fieldName, processHead));
                filedVo.setIsDisplay(true);
                filedVo.setIsEdit(false);
                filedVos.add(filedVo);
            }
        } else {
            //循环需要控制的权限字段，如果在数据库中查出来了。 就以数据库的为准， 如果没查出来。就默认显示不可编辑
            for (String fieldName : authFileds) {
                Boolean hasSet = false;
                for (PsCAppStepPmi pmi : stepPmis) {
                    //字段转驼峰，然后匹配如果存在则已数据库的权限设置为准,并删除掉此权限字段
                    camelField = Underline2Camel.underline2Camel(pmi.getcDisFld(), true);
                    if (fieldName.equals(camelField)) {
                        filedVo = new AuthorityFiledVO();
                        filedVo.setFieldKey(fieldName);
                        filedVo.setFieldValue(ObjectFieldUtils.getFieldValueByName(fieldName, processHead));
                        filedVo.setIsDisplay(pmi.getcVisibleSw().equals("Y"));
                        filedVo.setIsEdit(pmi.getcEditSw().equals("Y"));
                        filedVos.add(filedVo);
                        hasSet = true;
                    }
                }
                if (!hasSet) {
                    filedVo = new AuthorityFiledVO();
                    filedVo.setFieldKey(fieldName);
                    filedVo.setFieldValue(ObjectFieldUtils.getFieldValueByName(fieldName, processHead));
                    filedVo.setIsDisplay(true);
                    filedVo.setIsEdit(false);
                    filedVos.add(filedVo);
                }
            }
        }
        return filedVos;
    }

    /**
     * 根据待审批用户查询权限字段列表
     *
     * @param transNum
     * @param user
     * @return
     */
    public List<PsCAppStepPmi> getStepPmis(String transNum, UserDTO user) {
        PsCAppStepInst stepInst = getStepInst(transNum, user);
        if (stepInst == null) {
            return null;
        }
        //根据步骤表获取权限字段列表
        PsCAppStepPmi pmi = objectMapper.map(stepInst, PsCAppStepPmi.class);
        return psCAppStepPmiService.getAppStepPmi(pmi);
    }


    /**
     * 获取权限列表
     *
     * @param transNum
     * @return
     */
    public List<PsCAppStepPmi> getStepPmis(String transNum, BigDecimal stepNbr) {
        PsCAppStepInstPrimary instPrimary = new PsCAppStepInstPrimary();
        instPrimary.setcStepNbr(stepNbr);
        instPrimary.setcTransactionNbr(transNum);
        PsCAppStepInst stepInst = psCAppStepInstRepository.findOne(instPrimary);
        if (stepInst == null) {
            return null;
        }
        //根据步骤表获取权限字段列表
        PsCAppStepPmi pmi = objectMapper.map(stepInst, PsCAppStepPmi.class);
        return psCAppStepPmiService.getAppStepPmi(pmi);
    }

    /**
     * 获取用户的待审批流程审批步骤
     *
     * @param transNum 流程编号
     * @param user     用户信息
     * @return
     */
    public PsCAppStepInst getStepInst(String transNum, UserDTO user) {
        BigDecimal stepNbr = null;
        PsCAppUserInst userInst = new PsCAppUserInst();
        userInst.setcTransactionNbr(transNum);
        userInst.setcStepStatus(StepStatusEnum.STEP_STATUS_P.getStatus());
        //获取待审批/待阅的流程用户
        //先获取待审批
        List<PsCAppUserInst> userInsts = psCAppUserInstRepository.findAll(Example.of(userInst));
        if (userInsts == null || userInsts.isEmpty()) {
            userInst.setcStepStatus(StepStatusEnum.STEP_STATUS_UR.getStatus());
            userInsts = psCAppUserInstRepository.findAll(Example.of(userInst));
            if (userInsts == null || userInsts.isEmpty()) {
                return null;
            }
        }
        //判断当前用户是否为待审批/待阅的流程用户
        for (PsCAppUserInst inst : userInsts) {
            if (user.getOprId().equals(inst.getOprid())) {
                stepNbr = inst.getcStepNbr();
            }
        }
        if (stepNbr == null) {
            return null;
        }
        //获取流程审批步骤表内容
        PsCAppStepInstPrimary instPrimary = new PsCAppStepInstPrimary();
        instPrimary.setcStepNbr(stepNbr);
        instPrimary.setcTransactionNbr(transNum);
        PsCAppStepInst stepInst = psCAppStepInstRepository.findOne(instPrimary);
        return stepInst;
    }


    /* (non-Javadoc)
     * @see ProcessService#getTODOlist()
     */
    @Override
    public List<PsCWfStatusVw> getTODOlist(PsCWfStatusVw wfStatus) {
        Specification<PsCWfStatusVw> speci = (root, query, builder) -> {
            Predicate opridEqul = builder.equal(root.get("oprid"), wfStatus.getOprid());
            List<String> userStatusList = new ArrayList<String>();
            userStatusList.add("P");
            userStatusList.add("UR");
            //A-待处理，UR-待阅
            Predicate userStatusIn = root.get("cUserStatus").in(userStatusList); //绑定状态

            List<String> cWfTypeList = new ArrayList<>();
            cWfTypeList.add("DRFT");
            cWfTypeList.add("PRC");
            cWfTypeList.add("XFR");
            cWfTypeList.add("TER");

            cWfTypeList.add("LAVE");
            cWfTypeList.add("SEL");
            cWfTypeList.add("OUT");
            cWfTypeList.add("OVRT");
            cWfTypeList.add("REC");
            Predicate wfTypeIn = root.get("cWfType").in(cWfTypeList); //绑定状态

            Predicate equlAndIn = builder.and(opridEqul, userStatusIn);
            equlAndIn = builder.and(equlAndIn, wfTypeIn);
            if (StringUtils.isNotBlank(wfStatus.getText254())) {
                return builder.and(equlAndIn, builder.like(root.get("text254"), "%" + wfStatus.getText254() + "%"));
            } else {
                return builder.and(equlAndIn);
            }
        };
        return psCWfStatusVwRepository.findAll(speci, new Sort(Sort.Direction.DESC, "createdttm"));
    }


    /* (non-Javadoc)
     * @see ProcessService#getApprovedlist(PsCWfStatusVw)
     */
    @Override
    public List<PsCWfStatusVw> getApprovedlist(PsCWfStatusVw wfStatus) {
        Specification<PsCWfStatusVw> speci = (root, query, builder) -> {
            Predicate opridEqul = builder.equal(root.get("oprid"), wfStatus.getOprid());
            List<String> userStatusList = new ArrayList<String>();
            userStatusList.add("A");
            userStatusList.add("D");
            userStatusList.add("B");
            userStatusList.add("R");
            userStatusList.add("J");
            userStatusList.add("T");
            userStatusList.add("AR");
            //A-同意，D-不同意，B-已退回，R-重新提交，J-加签，T-拒绝，AR-已阅
            Predicate userStatusIn = root.get("cUserStatus").in(userStatusList); //绑定状态


            List<String> cWfTypeList = new ArrayList<>();
            cWfTypeList.add("DRFT");
            cWfTypeList.add("PRC");
            cWfTypeList.add("XFR");
            cWfTypeList.add("TER");

            cWfTypeList.add("LAVE");
            cWfTypeList.add("SEL");
            cWfTypeList.add("OUT");
            cWfTypeList.add("OVRT");
            cWfTypeList.add("REC");
            Predicate wfTypeIn = root.get("cWfType").in(cWfTypeList); //流程类型

            Predicate EqulAndIn = builder.and(opridEqul, userStatusIn);
            EqulAndIn = builder.and(EqulAndIn, wfTypeIn);
            if (StringUtils.isNotBlank(wfStatus.getText254())) {
                return builder.and(EqulAndIn, builder.like(root.get("text254"), "%" + wfStatus.getText254() + "%"));
            } else {
                return builder.and(EqulAndIn);
            }
        };
        List<PsCWfStatusVw> result = psCWfStatusVwRepository.findAll(speci, new Sort(new Sort.Order(Sort.Direction.DESC, "cWfLastupddttm").nullsLast()));
        result.sort((PsCWfStatusVw o2, PsCWfStatusVw o1) -> {
            if (o1.getcWfLastupddttm() != null && o2.getcWfLastupddttm() != null) {
                return o1.getcWfLastupddttm().compareTo(o2.getcWfLastupddttm());
            } else if (o1.getcWfLastupddttm() == null && o2.getcWfLastupddttm() == null) {
                if (o1.getCreatedttm() != null && o2.getCreatedttm() != null) {
                    return o1.getCreatedttm().compareTo(o2.getCreatedttm());
                }
            } else if (o1.getcWfLastupddttm() == null) {
                return -1;
            } else if (o2.getcWfLastupddttm() == null) {
                return 1;
            }
            return 0;
        });
        return result;
    }


    /* (non-Javadoc)
     * @see ProcessService#getMyCreatedProcess(com.auxgroup.hr.front.commons.vo.UserDTO)
     */
    @Override
    public List<PsCWfCatalog> getMyCreatedProcess(PsCWfCatalog queryCata) {
        Specification<PsCWfCatalog> speci = new Specification<PsCWfCatalog>() {
            @Override
            public Predicate toPredicate(Root<PsCWfCatalog> root,
                                         CriteriaQuery<?> query, CriteriaBuilder builder) {

                List<String> cWfTypeList = new ArrayList<>();
                cWfTypeList.add("DRFT");
                cWfTypeList.add("PRC");
                cWfTypeList.add("XFR");
                cWfTypeList.add("TER");

                cWfTypeList.add("LAVE");
                cWfTypeList.add("SEL");
                cWfTypeList.add("OUT");
                cWfTypeList.add("OVRT");
                cWfTypeList.add("REC");
                Predicate wfTypeIn = root.get("cWfType").in(cWfTypeList); //流程类型

                Predicate opridEqul = builder.equal(root.get("createdBy"), queryCata.getCreatedBy());
                opridEqul = builder.and(opridEqul, wfTypeIn);
                if (StringUtils.isNotBlank(queryCata.getText254())) {
                    return builder.and(opridEqul, builder.like(root.get("text254"), "%" + queryCata.getText254() + "%"));
                } else {
                    return builder.and(opridEqul);
                }
            }
        };
        return psCWfCatalogRepository.findAll(speci, new Sort(Sort.Direction.DESC, "createdttm"));
    }

}
 